
#ifndef AFTERMATH_ALGEBRA_MIXED_NUMERAL_HPP_INCLUDED
#define AFTERMATH_ALGEBRA_MIXED_NUMERAL_HPP_INCLUDED

#include <cmath>
#include <cstdint>
#include <ostream>
#include <string>

namespace aftermath
{
    namespace algebra
    {
        template <std::size_t t_denominator>
        struct count_digits
        {
            static const std::size_t value = 1UL + count_digits<t_denominator / 10>::value;
            static const std::size_t numerator_value = 1UL + count_digits<(t_denominator - 1) / 10>::value;
        };

        template <>
        struct count_digits<0UL>
        {
            static const std::size_t value = 0UL;
            static const std::size_t numerator_value = 0UL;
        };

        template <>
        struct count_digits<1UL>
        {
            static const std::size_t value = 1UL;
            static const std::size_t numerator_value = 0UL;
        };

        template <>
        struct count_digits<2UL>
        {
            static const std::size_t value = 1UL;
            static const std::size_t numerator_value = 1UL;
        };

        // A "presenter" class for floating-point numbers;
        // approximates a positive number (x) with (w + a / t_denominator), where (w) is an integer, and 0 <= a < t_denominator,
        // written as "w_b", where (b) is (a) padded on the right with zeros.
        template <std::size_t t_denominator>
        struct mixed_numeral
        {
            static const std::size_t denominator = t_denominator;
            typedef mixed_numeral<denominator> type;
            static const std::size_t decimal_width = count_digits<t_denominator>::numerator_value;

        private:
            bool m_is_negative;
            std::size_t m_whole;
            std::size_t m_numerator;

        public:
            explicit mixed_numeral(double value)
                : m_is_negative(value < 0.0), m_whole(0UL), m_numerator(0UL)
            {
                if (value < 0.0) value = -value;

                this->m_whole = static_cast<std::size_t>(value);
                value -= this->m_whole;
                value *= t_denominator;
                this->m_numerator = static_cast<std::size_t>(std::round(value));
                if (this->m_numerator == t_denominator)
                {
                    this->m_numerator = 0;
                    this->m_whole++;
                }
            }

            friend std::ostream& operator <<(std::ostream& os, const type& that)
            {
                if (that.m_is_negative) os << '-';
                os << that.m_whole;
                if (type::decimal_width != 0UL)
                {
                    os << '_';

                    std::size_t ten = npow<10, type::decimal_width - 1UL>::value;
                    while (ten != 1UL && that.m_numerator / ten == 0UL)
                    {
                        os << '0';
                        ten /= 10UL;
                    }

                    os << that.m_numerator;
                }
                return os;
            }
        };
    }
}

#endif // AFTERMATH_ALGEBRA_MIXED_NUMERAL_HPP_INCLUDED
